package hydrap2p.gui;

import hydrap2p.download.DownloadPriority;
import hydrap2p.download.Source;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;

/**
 * Represents a single download, including a list of contained files, associated
 * labels, and various specific elements (like the hash or the size)
 * 
 * @author Jared
 */
public class GUIDownloadListing {

	private String name; // the display string
	private String status; // this will probably be converted to an enum or
							// something in the future
	private DownloadPriority priority; // how important this download is
	private long size; // the size in bytes

	private String protocol; // BitTorrent, HTTP, UseNet
	private long bytesDownloaded; // how many bytes the user has downloaded
	private long bytesUploaded; // how many bytes the user has uploaded
	private String downloadLocation; // where the file is being saved to
	private String hash; // the unique hash for the download
	private int seedCount; // how many seeds the user is connected to
	private int peerCount; // how many peers are also downloading this
	private List<Source> sources;

	private long totalTimeUntilPause; // Total active time elapsed before
										// download was last inactive
	private long timeOfLastPause; 	// Timestamp of when download was last
									// inactive
	private Map<String, Long> downloadTimestamps;		//Most recent chunk completion time for a source.  Keyed by source IP.
	private Map<String, Long> uploadTimestamps; 
	
	private Map<String, Long> downloadSpeeds;	//Most recent speed calculation for a source
	private Map<String, Long> uploadSpeeds;
	
	// Statuses
	public static final String PAUSED = "PAUSED";
	public static final String ACTIVE = "ACTIVE";
	public static final String DONE = "DONE";

	// lists of labels and files
	private ArrayList<String> labels = new ArrayList<String>();
	private ArrayList<String> contents = new ArrayList<String>();
	
	/**
	 * A full constructor that requires everything and sets everything. This is
	 * used for creating the file transfer panel entries.
	 */
	public GUIDownloadListing(String name, String protocol, String hash, String status, String location, int sCount,
			int pCount, DownloadPriority priority, long size, long bDown, long bUp) {
		this.name = name;
		this.status = status;
		this.priority = priority;
		this.size = size;
		this.protocol = protocol.toUpperCase();
		this.bytesDownloaded = bDown;
		this.bytesUploaded = bUp;
		this.downloadLocation = location;
		this.seedCount = sCount;
		this.peerCount = pCount;
		this.hash = hash;

		sources = new ArrayList<Source>();
		downloadTimestamps = new TreeMap<String, Long>();
		uploadTimestamps = new TreeMap<String, Long>();
		
		downloadSpeeds = new TreeMap<String, Long>();
		uploadSpeeds = new TreeMap<String, Long>();
		
		totalTimeUntilPause = 0;
		timeOfLastPause = System.currentTimeMillis();
	}

	/**
	 * A simplified constructor that only sets the values required to make it
	 * show up in the GUI. This constructor is used to generate mock search
	 * results, so it doesn't need anything except name and protocol
	 * 
	 * @param name
	 *            the name to display in the results table
	 * @param protocol
	 *            the protocol that this download uses
	 */
	/*
	 * public GUIDownloadListing(String name, String protocol) { this(name,
	 * protocol, "HASH", "NONE", "intarwebz", 0, 0, 0, 0, 0, 0); }
	 */

	// setters
	public void setPriority(DownloadPriority newPriority) {
		priority = newPriority;
	}

	public void setStatus(String newStatus) {
		status = newStatus;
	}

	public void addContent(String contentName) {
		contents.add(contentName);
	}
	
	public void updateDownloadSpeed(String sourceIp, long bytes) {
		long now = System.currentTimeMillis();
		if(!downloadTimestamps.containsKey(sourceIp)) {
			downloadTimestamps.put(sourceIp, now);
		} else {
			long timestamp = downloadTimestamps.get(sourceIp);
			if(bytes > 0) {
				downloadSpeeds.put(sourceIp, (bytes * 1000) / (now - timestamp));
			} else {
				downloadSpeeds.put(sourceIp, 0L);
			}
			
			downloadTimestamps.put(sourceIp, now);
		}
	}
	
	public void updateUploadSpeed(String sourceIp, long bytes) {
		long now = System.currentTimeMillis();
		if(!uploadTimestamps.containsKey(sourceIp)) {
			uploadTimestamps.put(sourceIp, now);
		} else {
			long timestamp = uploadTimestamps.get(sourceIp);
			if(bytes > 0) {
				uploadSpeeds.put(sourceIp, (bytes * 1000) / (now - timestamp));
			} else {
				downloadSpeeds.put(sourceIp, 0L);
			}
			
			uploadTimestamps.put(sourceIp, now);
		}
	}

	public ArrayList<String> getContents() {
		return contents;
	}

	public String getProtocol() {
		return protocol;
	}

	public long getBytesDownloaded() {
		return bytesDownloaded;
	}

	public long getBytesUploaded() {
		return bytesUploaded;
	}

	public String getDownloadLocation() {
		return downloadLocation;
	}

	public String getHash() {
		return hash;
	}

	public int getSeedCount() {
		return seedCount;
	}

	public int getPeerCount() {
		return peerCount;
	}

	public long getDownloadSpeed() {
		long total = 0;
		for(Entry<String, Long> entry : downloadSpeeds.entrySet()) {
			total += entry.getValue();
		}
		
		return total;
	}

	public long getUploadSpeed() {
		long total = 0;
		for(Entry<String, Long> entry : uploadSpeeds.entrySet()) {
			total += entry.getValue();
		}
		
		return total;
	}

	public void setProtocol(String protocol) {
		this.protocol = protocol;
	}

	public void setBytesDownloaded(long bytesDownloaded) {
		this.bytesDownloaded = bytesDownloaded;
	}

	public void setBytesUploaded(long bytesUploaded) {
		this.bytesUploaded = bytesUploaded;
	}

	public void setDownloadLocation(String downloadLocation) {
		this.downloadLocation = downloadLocation;
	}

	public void setHash(String hash) {
		this.hash = hash;
	}

	public void setSeedCount(int seedCount) {
		this.seedCount = seedCount;
	}

	public void setPeerCount(int peerCount) {
		this.peerCount = peerCount;
	}

	// getters
	public String getName() {
		return name;
	}

	public String getStatus() {
		return status;
	}

	public DownloadPriority getPriority() {
		return priority;
	}

	public String getPriorityString() {
		return priority.toString();
	}

	public long getSize() {
		return size;
	}

	public void setSize(long bytes) {
		size = bytes;
	}

	public int getProgress() {
		return (int)(((bytesDownloaded + 0.0) / size) * 100);
	}

	public void addSource(Source source) {
		sources.add(source);
	}

	public List<Source> getSources() {
		return sources;
	}

	public void removeSource(Source source) {
		sources.remove(source);
	}

	// label operations
	public boolean addLabel(String label) {
		return labels.add(label);
	}

	public ArrayList<String> getLabels() {
		return labels;
	}

	public boolean hasLabel(String label) {
		return labels.contains(label);
	}

	public boolean removeLabel(String label) {
		return labels.remove(label);
	}

	public boolean hasNoLabels() {
		return labels.size() == 0;
	}

	public void printLabels() {
		System.out.println(labels);
	}

	public String getETAString() {
		long rawETA = getETA();
		if(rawETA < 60) {
			return rawETA + "s";
		} else if(rawETA >= 60 && rawETA < 3600) {
			return (rawETA / 60) + "m " + (rawETA % 60) + "s";
		} else if(rawETA >= 3600 && rawETA < 86400) {
			return (rawETA / 3600) + "h " + ((rawETA % 3600) / 60) + "m";
		} else if(rawETA >= 86400 && rawETA < 604800) {
			return (rawETA / 86400) + "d " + ((rawETA % 86400) / 3600) + "h";
		} else {

			return (rawETA / 604800) + "w " + ((rawETA % 604800) / 86400) + "d";
		}

	}

	public long getETA() {
		long now = System.currentTimeMillis();
		double totalTime = totalTimeUntilPause + (now - timeOfLastPause);
		double rawETA = (size * (totalTime / bytesDownloaded)) - totalTime;
		return (long)rawETA / 1000;

	}
	
	public String toString()
	{
		return name;
	}
}
